﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="WebProxy.cs" company="">
//   
// </copyright>
// <summary>
//   The web proxy.
// </summary>
// --------------------------------------------------------------------------------------------------------------------



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace cz.burger.windows.MDCCTaxi
{
    using System.IO;
    using System.Net;
    using System.Text.RegularExpressions;

    /// <summary>
    /// The web proxy.
    /// </summary>
    public class WebProxy: IWebProxy
    {
        /// <summary>
        /// The cookies.
        /// </summary>
        private CookieContainer cookies = new CookieContainer();

        private String BASEURL = "http://ibook.taxinord.dk/";

        private string OrderLink { get; set; }
        private string StatusLink { get; set; }

        private WebProxyData Data = new WebProxyData();

        private WebParser Parser = new WebParser();

        private String PrepareUrl(String relativeUrl = null)
        {
            var url = BASEURL;
            if (relativeUrl != null) url += HttpUtility.HtmlDecode(relativeUrl);
            return url;
        }

        private Task<WebProxyData> AwaitHttpRequest(string url, AsyncCallback callback)
        {
            var tcs = new TaskCompletionSource<WebProxyData>();
            var request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";
            request.CookieContainer = cookies;
            request.BeginGetResponse(callback, new WebProxyProcess() { Request = request, Action = data => tcs.SetResult(data) });
            return tcs.Task;
        }

        /// <summary>
        /// The login.
        /// </summary>
        public Task<WebProxyData> Login()
        {
            return AwaitHttpRequest(this.PrepareUrl(), this.ProcessLoginResponse);
        }

        public Task<WebProxyData> LoadStatus()
        {
            return AwaitHttpRequest(this.StatusLink, this.ProcessStatusResponse);
        }

        private string ReadResponse(IAsyncResult result)
        {
            var process = (WebProxyProcess)result.AsyncState;
            var response = (HttpWebResponse)process.Request.EndGetResponse(result);
            Stream stream = response.GetResponseStream();
            var content = String.Empty;
            using (var reader = new StreamReader(stream))
            {
                content = reader.ReadToEnd();
            }
            return content;
        }

        /// <summary>
        /// The process login response.
        /// </summary>
        /// <param name="result">
        /// The result.
        /// </param>
        private void ProcessLoginResponse(IAsyncResult result)
        {
            var process = (WebProxyProcess)result.AsyncState;
            try
            {
                var content = this.ReadResponse(result);
                this.ProcessLoginContent(content, process.Action);
            }
            catch (Exception e)
            {
                this.Data.ErrorMessage = e.ToString();
                process.Action(this.Data);
            }
        }

        /// <summary>
        /// The process content.
        /// </summary>
        /// <param name="content">
        /// The content.
        /// </param>
        private void ProcessLoginContent(string content, Action<WebProxyData> action)
        {
            if (!Parser.IsLoginForm(content))
            {
                this.Data.ErrorMessage = "Failed to load login form";
                return;
            }

            string formAction;
            if (!Parser.GetLoginAction(content, out formAction))
            {
                this.Data.ErrorMessage = "Where is the form action?!?";
                return;
            }

            this.PerformLogin(action, this.PrepareUrl(formAction));
        }

        /// <summary>
        /// The perform login.
        /// </summary>
        private void PerformLogin(Action<WebProxyData> action, String url)
        {
            var request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.CookieContainer = cookies;
            request.BeginGetRequestStream(this.ProcessLoginStream, new WebProxyProcess() { Request = request, Action = action });
        }

        /// <summary>
        /// The process login stream.
        /// </summary>
        /// <param name="result">
        /// The result.
        /// </param>
        private void ProcessLoginStream(IAsyncResult result)
        {
            var process = (WebProxyProcess)result.AsyncState;
            var stream = process.Request.EndGetRequestStream(result);
            using (var writer = new StreamWriter(stream))
            {
                var postContent = String.Format("username={0}&password={1}", WebCredentials.USERNAME, WebCredentials.PASSWORD);
                writer.WriteLine(postContent);
            }

            process.Request.BeginGetResponse(this.ProcessLoginResult, process);
        }

        /// <summary>
        /// The process login result.
        /// </summary>
        /// <param name="result">
        /// The result.
        /// </param>
        private void ProcessLoginResult(IAsyncResult result)
        {
            var process = (WebProxyProcess)result.AsyncState;
            var content = ReadResponse(result);

            if (!Parser.IsOrderPage(content))
            {
                Data.ErrorMessage = "Login failed, there is no order page!";
                return;
            }
            
            this.Data.Logged = true;

            string orderLink;
            string statusLink;
            if (!Parser.GetMenuLinks(content, out orderLink, out statusLink))
            {
                Data.ErrorMessage = "Failed to process the page menu";
                return;
            }

            this.OrderLink = this.PrepareUrl(orderLink);
            this.StatusLink = this.PrepareUrl(statusLink);

            process.Action(this.Data);
        }

        private void ProcessStatusResponse(IAsyncResult result)
        {
            var process = (WebProxyProcess)result.AsyncState;
            var content = this.ReadResponse(result);

            List<string> rideTexts;
            if (!Parser.GetRideStrings(content, out rideTexts)) 
            {
                Data.ErrorMessage = "Failed to parse the status page!";
                return;
            }

            foreach (var str in rideTexts) 
            {
                RideData ride;
                if (Parser.GetRide(str, out ride)) {
                    Data.Rides.Add(ride);
                }
            }

            process.Action(this.Data);

        }

    }


}
